/*!
//      
//      \file	k2k_StateHandlerRstr.c
//      
//      \author	Copyright 2010 Valentina Vuksic <vuksv1@bfh.ch>, Beat Liechti <liecb2@bfh.ch>
//      
//      This program is free software; you can redistribute it and/or modify
//      it under the terms of the GNU General Public License as published by
//      the Free Software Foundation; either version 2 of the License, or
//      (at your option) any later version.
//      
//      This program is distributed in the hope that it will be useful,
//      but WITHOUT ANY WARRANTY; without even the implied warranty of
//      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//      GNU General Public License for more details.
//      
//      You should have received a copy of the GNU General Public License
//      along with this program; if not, write to the Free Software
//      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
//      MA 02110-1301, USA.
//
//      Abstract Data Type for the state handler of the state "Rstr" (Ristretto)
//      
//      \brief		CAS-EBX-03 Projektarbeit 
//      			Valentina Vuksic (VUKSV1), Beat Liechti (LIECB2)
//      			StateHandlerRstr ADT for the k2k (Kaffee2000) application
//      \version	1.0
//      \date		2010-04-11
//      \remark		Last Modifications:
//      \remark  	v1.0, VUKSV1/LIECB2, 2010-04-11 Initial release
//
*/

#include <stdio.h>
#include <unistd.h>

#include "k2k_StateHandlerRstr.h"
#include "k2k_AudioPlayer.h"

#define STATE_TEXT_1	"Making one ristretto"
#define STATE_TEXT_2	"Making two ristretti"

/* -- static function declarations -----------------------------------*/

static k2k_State k2k_StateHandlerRstr_Handler(k2k_Event event, k2k_State previousState, k2k_StateHandlerData *handlerData);
static void k2k_StateHandlerRstr_InitState(k2k_StateHandlerRstr *stateHandlerRstr, k2k_State previousState);
static k2k_State k2k_StateHandlerRstr_ExitState(k2k_StateHandlerRstr *stateHandlerRstr, k2k_State nextState);
static void k2k_StateHandlerRstr_MakeCoffee(k2k_StateHandlerRstr *stateHandlerRstr);
static void k2k_StateHandlerRstr_SignalHandler(int sigNo, void *customData);

/* -- ADT functions --------------------------------------------------*/

/*!
//
// Creates StateHandlerRstr ADT "object"
//
// \param stateMachine is the pointer to the StateMachine structure
// 
// \return pointer to the StateHandlerRstr ADT
//
*/
k2k_StateHandlerRstr* k2k_StateHandlerRstr_Create(k2k_StateMachine *stateMachine)
{
	k2k_StateHandlerRstr *stateHandlerRstr = NULL;
	stateHandlerRstr = malloc(sizeof(k2k_StateHandlerRstr));
	if (stateHandlerRstr != NULL)
	{
		/*! initialize structure */		
		stateHandlerRstr->handler = k2k_StateHandlerRstr_Handler;	
		stateHandlerRstr->coffeeCount = 0;
		stateHandlerRstr->coffeeDoneCount = 0;
		stateHandlerRstr->fillDegree = 0;
		/*! setup signaling */
		stateHandlerRstr->signal = k2k_Signal_Create(k2k_SIG_RSTR, k2k_StateHandlerRstr_SignalHandler, stateHandlerRstr);
		if (stateHandlerRstr->signal == NULL)
		{
			stateHandlerRstr->err = k2k_STATEHANDLER_ERR_CREATE_SIGNAL_FAILED;
		}
		else
		{
			/*! reset signal variable */
			stateHandlerRstr->sigNo = 0;
			/*! create state handler data */
			stateHandlerRstr->handlerData = k2k_StateHandlerData_Create(stateHandlerRstr);			
			if (stateHandlerRstr->handlerData == NULL)
			{
				stateHandlerRstr->err = k2k_STATEHANDLER_ERR_CREATE_HANDLERDATA_FAILED;
			}
			else
			{		
				/*! add the state handler to the state machine */
				k2k_StateMachine_AddStateHandler(stateMachine, STATE_RSTR, stateHandlerRstr->handler, stateHandlerRstr->handlerData);
				/*! store reference to state machine */
				stateHandlerRstr->stateMachine = stateMachine;			
				/*! show icon */
				k2k_NanoX_ShowRstrIcon(stateMachine->nanoX);
				/*! flag success */
				stateHandlerRstr->err = k2k_STATEHANDLER_ERR_SUCCESS;
			}
		}
	}
	return stateHandlerRstr;
}

/*!
//
// Releases StateHandlerOff ADT "object"
// 
// \param stateHandlerRstr is the pointer to the StateHandlerRstr structure 
// 
// \return nothing
//
*/
void k2k_StateHandlerRstr_Release(k2k_StateHandlerRstr *stateHandlerRstr)
{
	if (stateHandlerRstr != NULL)
	{		
		if (stateHandlerRstr->signal != NULL)
		{
			/*! release signal ADT */
			k2k_Signal_Release(stateHandlerRstr->signal);
			stateHandlerRstr->signal = NULL;
		}
		if (stateHandlerRstr->handlerData != NULL)
		{
			/*! release handler data */
			k2k_StateHandlerData_Release(stateHandlerRstr->handlerData);
			stateHandlerRstr->handlerData = NULL;
		}
		/*! release memory */
		free(stateHandlerRstr);	
	}
}

/* -- static functions -----------------------------------------------*/

/*!
//
// Actual state handler for the state "Rstr"
//
// \param event describes the event that happened
// \param previousState describes the previous state
// \param handlerData points to a structure for custom data 
// 		  (typically containing a pointer the corresponding ADT struct)
//
// \return next state of the event machine
//
*/
static k2k_State k2k_StateHandlerRstr_Handler(k2k_Event event, k2k_State previousState, k2k_StateHandlerData *handlerData)
{
	/*! get the state handler structure */
	k2k_StateHandlerRstr *stateHandlerRstr = handlerData->customData;
	
	/*! are we entering the state? */
	if (previousState == STATE_RDY1 || previousState == STATE_RDY2)
	{			
		/*! initialize state */
		k2k_StateHandlerRstr_InitState(stateHandlerRstr, previousState);
		/*! make a coffee */
		k2k_StateHandlerRstr_MakeCoffee(stateHandlerRstr);
	}	
	
	/*! the default is to remain in the same state */	
	int nextState = STATE_RSTR;
	switch (event)
	{
		case EVENT_S1:
			/*! go into "Off" state */
			nextState = k2k_StateHandlerRstr_ExitState(stateHandlerRstr, STATE_OFF);
			break;		
		case EVENT_TIMER:
			/*! check if signal has arrived */
			if (stateHandlerRstr->sigNo != 0)
			{
				/*! count the coffe made */
				stateHandlerRstr->coffeeDoneCount++;
				/*! count the coffe globally */	
				k2k_StateMachine_CountOneCoffee();				
				/*! make the second coffee or go into "Rdy1" state */
				if (stateHandlerRstr->coffeeDoneCount < stateHandlerRstr->coffeeCount)
				{
					/*! make another coffee */
					k2k_StateHandlerRstr_MakeCoffee(stateHandlerRstr);
				}		
				else
				{
					/*! go into "Rdy1" state */
					nextState = k2k_StateHandlerRstr_ExitState(stateHandlerRstr, STATE_RDY1);
				}
			}
			else
			{
				/*
				stateHandlerRstr->timerCount++;
				if (stateHandlerRstr->timerCount > 150)
				{
					k2k_StateMachine_SetGrinderSpeed(stateHandlerRstr->stateMachine, 0);
					k2k_StateMachine_SetMilkPumpAmount(stateHandlerRstr->stateMachine, 0);
				}
				*/				
				/*! blink the cups */
				k2k_NanoX_BlinkCups(stateHandlerRstr->stateMachine->nanoX, stateHandlerRstr->fillDegree, stateHandlerRstr->coffeeCount, stateHandlerRstr->coffeeDoneCount);
			}
			break;
		default:
			break;
	}
	
	/*! update animation for activity */
	k2k_NanoX_UpdateRstrAnimation(stateHandlerRstr->stateMachine->nanoX, &stateHandlerRstr->fillDegree);	
	
	return nextState;
}

/*!
//
// Initialize the state, called when enetering from another state
//
// \param stateHandlerRstr is the pointer to the StateHandlerRstr structure 
// \param previousState describes the previous state
//
// \return nothing
//
*/
static void k2k_StateHandlerRstr_InitState(k2k_StateHandlerRstr *stateHandlerRstr, k2k_State previousState)
{
	/*! initialization depends on what the previous state was */
	if (previousState == STATE_RDY1)
	{
		/*! make one ristretto */
		stateHandlerRstr->coffeeCount = 1;
		printf("Making one ristretto...\n");
		/*! set state text */
		k2k_NanoX_ShowStateText(stateHandlerRstr->stateMachine->nanoX, STATE_TEXT_1);
	}
	else
	{
		/*! make two ristretti */		
		stateHandlerRstr->coffeeCount = 2;
		printf("Making two ristretti...\n");
		/*! set state text */
		k2k_NanoX_ShowStateText(stateHandlerRstr->stateMachine->nanoX, STATE_TEXT_2);
	}
	/*! reset coffee count */
	stateHandlerRstr->coffeeDoneCount = 0;
	/*! start with empty animation */
	stateHandlerRstr->fillDegree = 0;
}

/*!
//
// Leave the state, called when conditions are met to exit the state 
//
// \param stateHandlerRstr is the pointer to the StateHandlerRstr structure 
// \param nextState describes the next state
//
// \return nothing
//
*/
static k2k_State k2k_StateHandlerRstr_ExitState(k2k_StateHandlerRstr *stateHandlerRstr, k2k_State nextState)
{
	k2k_StateMachine_SetGrinderSpeed(stateHandlerRstr->stateMachine, 0);
	k2k_StateMachine_SetMilkPumpAmount(stateHandlerRstr->stateMachine, 0);
	/*! disable signal handler */
	k2k_Signal_Disable(stateHandlerRstr->signal);
	/*! clear animation by setting fill degree to zero */
	stateHandlerRstr->fillDegree = 0;
	/*! go into "Off" state */
	return nextState;
}

/*!
//
// "Make" a coffee
//
// \param stateHandlerRstr is the pointer to the StateHandlerRstr structure 
// \param previousState describes the previous state
//
// \return nothing
//
*/
static void k2k_StateHandlerRstr_MakeCoffee(k2k_StateHandlerRstr *stateHandlerRstr)
{
	stateHandlerRstr->timerCount = 0;
	k2k_StateMachine_SetGrinderSpeed(stateHandlerRstr->stateMachine, 10);
	k2k_StateMachine_SetMilkPumpAmount(stateHandlerRstr->stateMachine, 0);
	/*! reset signal variable */
	stateHandlerRstr->sigNo = 0;
	/*! enable signal handler */
	k2k_Signal_Enable(stateHandlerRstr->signal);
	/*! play clip ==> "making the coffee" */
	k2k_AudioPlaySignal(CLIP_RSTR, stateHandlerRstr->signal);
}

/*!
//
// Signal handler for the state "Rstr" to synchronize with child process
//
// \param sigNo describes the signal sent by the child process
// \param customData points to custom data (typically the corresponding ADT struct)
//
// \return nothing
//
*/
static void k2k_StateHandlerRstr_SignalHandler(int sigNo, void *customData)
{	
	k2k_StateHandlerRstr *stateHandlerRstr = customData;
	stateHandlerRstr->sigNo = sigNo;
	printf("\nk2k_StateHandlerRstr_SignalHandler: received 0x%08X\n", sigNo);
}
